AuthenticationManager 认证原理分析 (Spring Security)

您所在的位置:网站首页 spring security认证原理 AuthenticationManager 认证原理分析 (Spring Security)

AuthenticationManager 认证原理分析 (Spring Security)

2023-08-12 16:43| 来源: 网络整理| 查看: 265

AuthenticationManager 认证原理分析 说明概述代码执行流程认证函数执行流程图总结

说明

AuthenticationManager 是 Spring Security提供的一套认证服务 类运行流程图: 在这里插入图片描述

概述

上述是AuthenticationManager 认证流程的类流程,现在咱们逐个分析这些类 1.SysLoginController:登录控制器 2.SysLoginService 登录业务类 3. WebSecurityConfigurerAdapter:网络安全配置器适配器 AuthenticationManagerDelegator(内部类):认证管理器委托人 4.ProviderManager :提供者管理器 5.AbstractUserDetailsAuthenticationProvider: 抽象用户详细信息身份验证提供程序 6.DaoAuthenticationProvider:数据层认证提供者 7.UserDetailsServiceImpl:用户详细信息服务实现

原理分析:根据类运行流程图,我们可以直接得到实现原理为,登录控制器1.发送登录请求2.执行登录业务类同时3.执行 网络安全配置器适配器中的适配一个认证管理器委托人4.再通过提供者管理器5.调用 抽象用户详细信息身份验证提供程序6.再调用数据库层认证提供者再7.调用用户详细信息服务实现

代码执行流程

1.首先进入自定义的业务验证的方法中(SysLoginController类)

/** * 登录方法 * * @param loginBody 登录信息 * @return 结果 */ @PostMapping("/login") public AjaxResult login(@RequestBody LoginBody loginBody) { AjaxResult ajax = AjaxResult.success(); // 生成令牌 String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid()); ajax.put(Constants.TOKEN, token); return ajax; }

2.进入该函数,执行authenticate函数(SysLoginService类)

public String login(String username, String password, String code, String uuid) { boolean captchaOnOff = configService.selectCaptchaOnOff(); // 验证码开关 if (captchaOnOff) { //验证验证码是否正确 validateCaptcha(username, code, uuid); } // 用户验证 Authentication authentication = null; try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername ★ authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (Exception e) { if (e instanceof BadCredentialsException) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); throw new UserPasswordNotMatchException(); } else { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); throw new ServiceException(e.getMessage()); } } AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId()); // 生成token return tokenService.createToken(loginUser); }

3.调用authenticate方法(AuthenticationManagerDelegator类(WebSecurityConfigurerAdapter的内部类))

public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (this.delegate != null) { ★ return this.delegate.authenticate(authentication); } else { synchronized(this.delegateMonitor) { if (this.delegate == null) { this.delegate = (AuthenticationManager)this.delegateBuilder.getObject(); this.delegateBuilder = null; } } ★ return this.delegate.authenticate(authentication); }

4.匹配对应的认证器,继续调用认证器中的authenticate方法(ProviderManager类)ProviderManager类中提供了,认证管理器集合,和父认证管理器来处理认证。 在这里插入图片描述

private static final Log logger = LogFactory.getLog(ProviderManager.class); private AuthenticationEventPublisher eventPublisher = new NullEventPublisher(); //认证管理器集合 ★private List providers = Collections.emptyList(); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); //父认证管理器 ★private AuthenticationManager parent; private boolean eraseCredentialsAfterAuthentication = true; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; int currentPosition = 0; int size = this.providers.size(); for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)", provider.getClass().getSimpleName(), ++currentPosition, size)); } try { //调用对应的认证管理器 ★ result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException | InternalAuthenticationServiceException ex) { prepareException(ex, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw ex; } catch (AuthenticationException ex) { lastException = ex; } } if (result == null && this.parent != null) { // Allow the parent to try. try { //调用对应的认证管理器 ★ parentResult = this.parent.authenticate(authentication); result = parentResult; } catch (ProviderNotFoundException ex) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException ex) { parentException = ex; lastException = ex; } } if (result != null) { if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } // If the parent AuthenticationManager was attempted and successful then it // will publish an AuthenticationSuccessEvent // This check prevents a duplicate AuthenticationSuccessEvent if the parent // AuthenticationManager already published it if (parentResult == null) { this.eventPublisher.publishAuthenticationSuccess(result); } return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } // If the parent AuthenticationManager was attempted and failed then it will // publish an AbstractAuthenticationFailureEvent // This check prevents a duplicate AbstractAuthenticationFailureEvent if the // parent AuthenticationManager already published it if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }

5.调用认证器中的authenticate认证方法(AbstractUserDetailsAuthenticationProvider 类) 在这里插入图片描述

@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); String username = determineUsername(authentication); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { //验证用户详细信息 ★ user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException ex) { this.logger.debug("Failed to find user '" + username + "'"); if (!this.hideUserNotFoundExceptions) { throw ex; } throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { this.preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException ex) { if (!cacheWasUsed) { throw ex; } // There was a problem, so try again after checking // we're using latest data (i.e. not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); this.preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } this.postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (this.forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }

6.加载用户名认证函数(DaoAuthenticationProvider类) 在这里插入图片描述

@Override protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); try { ★ UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } }

7.执行认证函数(UserDetailsServiceImpl类 实现UserDetailsService)

@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ★ SysUser user = userService.selectUserByUserName(username); if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new ServiceException("登录用户:" + username + " 不存在"); } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } return createLoginUser(user); }

7.1执行验证用户名密码函数(DaoAuthenticationProvider类)

@Override @SuppressWarnings("deprecation") protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { this.logger.debug("Failed to authenticate since no credentials provided"); throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); ★ if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { this.logger.debug("Failed to authenticate since password does not match stored value"); throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } 认证函数执行流程图

在这里插入图片描述

总结

AuthenticationManager 主要就是ProviderManager中提供一个认证管理器集合ist providers和父认证管理器 AuthenticationManager parent,匹配对应的认证管理器,然后再调用dao层用户信息匹配(用户名,密码),达到认证用户信息的功能

如果没有批评,赞美将毫无意义,欢迎指正批评。

路漫漫其修远兮,吾将上下而求索



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3